#!/usr/bin/env node
const path = require('path')
const { Readable } = require('stream')
const Koa = require('koa')
const send = require('koa-send')
const compilerSFC = require('@vue/compiler-sfc') //用于编译单文件组件
const Router = require('koa-router');
const app = new Koa()
const cwd = process.cwd()
const router = new Router()
// 将流转化成字符串
const streamToString = stream => new Promise((resolve, reject) => {
    const chunks = []
    stream.on('data', chunk => chunks.push(chunk))
    stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')))
    stream.on('error', reject)
  })

  const stringToStream = text => {//字符串转换为流
    const stream = new Readable()
    stream.push(text)
    stream.push(null)
    return stream
  }
// 3. 重写请求路径，/@modules/xxx => /node_modules/
app.use(async (ctx, next) => {
    if (ctx.path.startsWith('/@modules/')) {
        const moduleName = ctx.path.substr(10) // => vue
        // 加载包中的 package.json 文件
        const modulePkg = require(path.join(cwd, 'node_modules', moduleName, 'package.json'))
        //找到esmodule的入口文件 加载 package.json 中指定的 module 模块路径
        ctx.path = path.join('/node_modules', moduleName, modulePkg.module)
    }
    await next()
})

//1.0开启静态文件服务器

app.use(async(ctx, next) => {
    //返回当前目录也就是运行命令行工具目录下的index.html
    await send(ctx, ctx.path, {root:process.cwd(), index:'index.html'}) 
    await next()
})
// 4. 处理单文件组件 这里只处理vue单文件组件
//应该在处理静态文件之后 处理第三方模块之前
app.use(async (ctx, next) => {
    
    if (ctx.path.endsWith('.vue')) {
      const contents = await streamToString(ctx.body) //将流转换为字符串
      const { descriptor } = compilerSFC.parse(contents) //获取单文件组件的描述对象
      let code //最终返回给浏览器的代码
      if (!ctx.query.type) { //query不带type表示是第一次请求
        code = descriptor.script.content
        //将文件改造为vite中解析单文件组件的模式
        code = code.replace(/export\s+default\s+/g, 'const __script = ')

        //query不带type表示是第一次请求 这个type就是这里拼上去的
        code += `
        import { render as __render } from "${ctx.path}?type=template" 
        __script.render = __render
        export default __script
        `
      } else if (ctx.query.type === 'template') {//不是第一次请求
        const templateRender = compilerSFC.compileTemplate({ source: descriptor.template.content })
        code = templateRender.code
      }
      ctx.type = 'application/javascript'
      ctx.body = stringToStream(code)//字符串转换为流
    }
    await next()
  })
//2.0修改第三方模块的路径
app.use(async(ctx, next) => {
    if (ctx.type === 'application/javascript') {
        const contents = await streamToString(ctx.body)
        // import vue from 'vue' //通过下面进行路径重写
        // import App from './App.vue' 可以正常加载
        ctx.body = contents
        //匹配 from ' 替换为 from '@modules/
        .replace(/(from\s+['"])(?![\.\/])/g, '$1/@modules/')
        .replace(/process\.env\.NODE_ENV/g, '"development"') 
        // 替换process对象，因为我们是在浏览器中执行 process是node中的对象
        //所以我们需要将所有的process.env.NODE_ENV全部填换
    }
    await next()
})

router.get('/html.html', async (ctx, next) => {
  const code = await streamToString(ctx.body)
  console.log(ctx.type, ctx.path, code)
  ctx.body = '中间件'
});
  
app
  .use(router.routes())
  .use(router.allowedMethods());

app.listen(3000, () => {
    console.log('app is running @ http://localhost:3000')
})